# -*- coding: iso-8859-15 -*-
#
# LICENSE:
# 
# Copyright 2008 Håkan Lindqvist. All rights reserved.
# 
# Redistribution and use in source and binary forms, 
# with or without modification, are permitted provided 
# that the following conditions are met:
# 
#    1. Redistributions of source code must retain the 
#       above copyright notice, this list of conditions 
#       and the following disclaimer.
#    2. Redistributions in binary form must reproduce the 
#       above copyright notice, this list of conditions 
#       and the following disclaimer in the documentation 
#       and/or other materials provided with the distribution.
# 
# THIS SOFTWARE IS PROVIDED BY HÅKAN LINDQVIST ``AS IS'' 
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL HÅKAN LINDQVIST 
# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 
# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 
# DAMAGE.
# 

from adt.report.Report                  import Report
from reportlab.lib                      import colors
from reportlab.lib.colors               import Color
from reportlab.lib.enums                import TA_CENTER, TA_LEFT
from reportlab.lib.pagesizes            import A4
from reportlab.lib.styles               import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units                import cm
from reportlab.pdfgen                   import canvas
from reportlab.platypus                 import PageBreak, Paragraph, Spacer, PageBreak, SimpleDocTemplate
from reportlab.platypus.doctemplate     import BaseDocTemplate, _doNothing, PageTemplate
from reportlab.platypus.frames          import Frame
from reportlab.platypus.tables          import TableStyle
from reportlab.platypus.tableofcontents import TableOfContents
from reportlab.rl_config                import defaultPageSize

#################################################
# Functions used for formatting report pages
def titlePageFormatter(canvas, doc):
	pass

def contentPageFormatter(canvas, doc):
	canvas.saveState()
	canvas.drawCentredString(defaultPageSize[0]/2, 0.75 * cm, "%d" % doc.page)
	canvas.restoreState()

def getReportStyleSheet():
	return getSampleStyleSheet()

#################################################
# Classes used to provide some wrapping around the immense amount
# of imports needed to use reportlab

class VisitDocTemplate(SimpleDocTemplate):
	"""The document template used for all PDF documents to get the
	   table of contents to build correctly and still be able to use
	   custom formatters for the first and later pages.
	"""

	def afterFlowable(self, flowable):
		"""Registers TOC entries and makes outline entries."""

		if flowable.__class__.__name__ == 'Paragraph':
			styleName = flowable.style.name

			# Register TOC entries that are [hH]eadingN
			if styleName[:1] == 'h' or styleName[:1] == 'H':
				level   = int(styleName[-1]) - 1
				text    = flowable.getPlainText()
				pageNum = self.page
				self.notify('TOCEntry', (level, text, pageNum))

				# Add PDF outline entries (not really needed/tested here).
				key = str(hash(flowable))
				c   = self.canv
				c.bookmarkPage(key)
				c.addOutlineEntry(text, key, level=level, closed=0)

	def multiBuild(self,flowables,onFirstPage=_doNothing, onLaterPages=_doNothing, canvasmaker=canvas.Canvas):
		"""Build the document using the flowables. Annotate the first 
		   page using the onFirstPage function and later pages using 
		   the onLaterPages function. The onXXX pages should follow the 
		   signature.
	
		   def myOnFirstPage(canvas, document):
		   # do annotations and modify the document
		   ...
	
		   The functions can do things like draw logos, page numbers,
		   footers, etc. They can use external variables to vary the 
		   look (for example providing page numbering or section names).
		"""
		self._calc() #in case we changed margins sizes etc
	
		frameT = Frame(self.leftMargin, self.bottomMargin, self.width, self.height, id='normal')
	
		self.addPageTemplates([PageTemplate(id='First', frames=frameT, onPage=onFirstPage, pagesize=self.pagesize), PageTemplate(id='Later', frames=frameT, onPage=onLaterPages, pagesize=self.pagesize)])
	
		if onFirstPage is _doNothing and hasattr(self,'onFirstPage'):
			self.pageTemplates[0].beforeDrawPage = self.onFirstPage
	
		if onLaterPages is _doNothing and hasattr(self,'onLaterPages'):
			self.pageTemplates[1].beforeDrawPage = self.onLaterPages
	
		BaseDocTemplate.multiBuild(self,flowables, canvasmaker=canvasmaker) 

class PDFReport(Report):
	"""Main ADT PDF report class. It simply collects entries and provides an
	   easy interface for persisting it to a document.
	"""
	
	def __init__(self, out=None, titlePage=None, indexPage=None, contentPages=None, pagesize=A4, template=VisitDocTemplate, tpf=titlePageFormatter, cpf=contentPageFormatter):
		# Accessed by methods in he upper class to provide
		# meta information about the report
		self.__mimetype = ('application', 'pdf')

		# Instance settings
		self.__pagesize = pagesize
		self.__template = template
		self.__out      = out
		self.__story    = []

		self.__titlePageFormatter   = tpf
		self.__contentPageFormatter = cpf

		if titlePage:
			self.append(titlePage)

		if indexPage:
			self.append(indexPage)

		if not contentPages:
			raise Exception("The parameter 'contentPages' must be specified")
		else:
			self.append(contentPages)

	def append(self, item):
		""" Append a ReportPage to the Report """
		for x in item.get():
			self.__story.append(x)

	def write(self, out=None):
		""" Write the report; if no output has been defined (i.e. is None),
		    the data will be written to stdout.
		"""
		if out:
			self.__out = out

		if not self.__out:
			self.__out = sys.stdout

		document = self.__template(self.__out, pagesize=self.__pagesize)
		document.multiBuild(self.__story, onFirstPage=self.__titlePageFormatter, onLaterPages=self.__contentPageFormatter)

class ReportContents:
	"""Base class for ADT report contents, it is a simple place holder
	   for Flowable objects.
	"""
	def __init__(self):
		self.items = []

	def get(self):
		"""Get the Flwables registered with this contents holder."""
		return self.items

	def add(self, item):
		"""Add an item to this holder. If a ReportContents instance, or rather
		   a subclass thereof, is added, the Flowable items from that instance
		   will be fetched and added instead.
		"""
		if isinstance(item, ReportContents):
			for x in item.get():
				self.items.append(x)
		else:
			self.items.append(item)

class ReportTitlePage(ReportContents):
	"""Report title page."""

	def __init__(self, title=None, titleColor=None, abstract=None):
		ReportContents.__init__(self)

		if not title:
			raise Exception("The 'title' parameter must contain a value.")

		top_space   = defaultPageSize[1]/5
		after_space = defaultPageSize[1]/2.5
		titleColor  = titleColor or Color(1,0,0)

		titlestyle  = ParagraphStyle(name='TitleHeader', alignment=TA_CENTER, fontName='Helvetica', fontSize=52, textColor=titleColor)

		self.add(Spacer(0, top_space))
		self.add(Paragraph(title, titlestyle))

		if abstract:
			abstractstyle = ParagraphStyle(name='AbstractStyle', fontSize=10, alignment=TA_CENTER, backColor=Color(0.95,0.95,0.95), borderWidth=0, borderColor=Color(0,0,0), borderPadding=8, bulletIndent=50)
			self.add(Spacer(0, after_space))
			self.add(Paragraph(abstract, abstractstyle))

		self.add(PageBreak())

class ReportIndex(ReportContents):
	"""Table of contents."""

	def __init__(self):
		ReportContents.__init__(self)

		toc            = TableOfContents()
		toc.tableStyle = TableStyle([('VALIGN', (0,0), (-1,-1), 'TOP'), ('BACKGROUND',(0,0),(-1,-1),Color(0.95,0.95,0.95)), ('FONT', (0,0),(-1,-1),'Times-Roman', 10)])

		self.add(toc)
		self.add(PageBreak())

class ReportPages(ReportContents):
	"""Normal report pages."""
	pass

# If used as a script, run a small test case
if __name__ == "__main__":
	styles   = getReportStyleSheet()
	title    = ReportTitlePage(title='Foo test report', abstract='A simple test report with an abstract that is only there to occupy some text.')
	contents = ReportPages()
	contents.add(Paragraph("Foo bar to report", styles['Heading1']))
	contents.add(Paragraph("bla bla bla",       styles['Normal']))
	contents.add(Paragraph("Foo bar to report", styles['Heading2']))
	contents.add(Paragraph("bla bla bla",       styles['Normal']))
	contents.add(Paragraph("Foo bar to report", styles['Heading3']))
	contents.add(Paragraph("bla bla bla",       styles['Normal']))
	contents.add(PageBreak())
	contents.add(Paragraph("Foo bar to report", styles['Heading1']))
	contents.add(Paragraph("bla bla bla",       styles['Normal']))
	contents.add(Paragraph("Foo bar to report", styles['Heading2']))
	contents.add(Paragraph("bla bla bla",       styles['Normal']))

	Report(out='test.pdf', titlePage=title, indexPage=ReportIndex(), contentPages=contents).write()

